/***********************************************************************
* Microsoft (R) Windows (R) Resource Compiler
*
* Copyright (c) Microsoft Corporation.	All rights reserved.
*
* File Comments:
*
*
***********************************************************************/

#include "rc.h"


static BOOL fFontDirRead = FALSE;

BOOL    bExternParse = FALSE;

WORD    language = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
LONG    version = 0;
LONG    characteristics = 0;

static int rowError = 0;
static int colError = 0;
static int idError = 0;


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  ParseError3() -                                                         */
/*                                                                          */
/*--------------------------------------------------------------------------*/

void
ParseError3(
    int id
    )
{
    // Don't get caught giving the same error over and over and over...
    if ((Nerrors > 0) && (idError == id) && (rowError == token.row) && (colError == token.col))
        quit(NULL);

    SendError(L"\n");
    SendError(Msg_Text);

    if (++Nerrors > 25)
        quit(NULL);

    rowError = token.row;
    colError = token.col;
    idError = id;
}

/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  ParseError2() -                                                         */
/*                                                                          */
/*--------------------------------------------------------------------------*/

void
ParseError2(
    int id,
    PWCHAR arg
    )
{
    // Don't get caught giving the same error over and over and over...
    if ((Nerrors > 0) && (idError == id) && (rowError == token.row) && (colError == token.col))
        quit(NULL);

    SendError(L"\n");
    SET_MSG(id, curFile, token.row, arg);
    SendError(Msg_Text);

    if (++Nerrors > 25)
        quit(NULL);

    rowError = token.row;
    colError = token.col;
    idError = id;
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  ParseError1() -                                                         */
/*                                                                          */
/*--------------------------------------------------------------------------*/

void
ParseError1(
    int id
    )
{
    // Don't get caught giving the same error over and over and over...
    if ((Nerrors > 0) && (idError == id) && (rowError == token.row) && (colError == token.col))
        quit(NULL);

    SendError(L"\n");
    SET_MSG(id, curFile, token.row);
    SendError(Msg_Text);

    if (++Nerrors > 25)
        quit(NULL);

    rowError = token.row;
    colError = token.col;
    idError = id;
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  GetFileName() -                                                         */
/*                                                                          */
/*--------------------------------------------------------------------------*/

/* Read in a filename from the RC file. */

VOID
SearchInclude(wchar_t *szFile, wchar_t *szActual)
{
    size_t cchFile;
    const wchar_t *pchVar;

    // We don't do absolute paths

    if ((szFile[0] == L'\\') || (szFile[0] == L'/') || (szFile[1] == L':')) {
        if (wcslen(szFile) >= _MAX_PATH) {
            szActual[0] = L'\0';
        } else {
            wcscpy(szActual, szFile);
        }

        return;
    }

    cchFile = wcslen(szFile) + 1;

    pchVar = pchInclude;

    while (*pchVar != L'\0') {
        size_t ich;

        // Copy the next include path component

        for (ich = 0; *pchVar != L'\0'; pchVar++) {
            if (*pchVar == L';') {
                pchVar++;
                break;
            }

            if (ich < _MAX_PATH) {
               szActual[ich++] = *pchVar;
            }
        }

        if ((ich == 0) || (ich == _MAX_PATH)) {
             // Skip empty paths (e.g. ";;")
             // Skip too long paths too

             continue;
        }

        // Find end of path string

        // Check first!  this is what _searchenv() messed up!

        if ((szActual[ich - 1] != L'\\') && (szActual[ich - 1] != L'/')) {
            if (ich < _MAX_PATH) {
               szActual[ich++] = L'\\';
            }
        }

        // We already know szFile does not start with a drive or abs. dir

        if ((ich + cchFile) >= _MAX_PATH) {
            continue;
        }

        wcscpy(szActual + ich, szFile);

        // Is the file here? szActual already contains name

        if (!_waccess(szActual, 0)) {
            return;
        }
    }

    szActual[0] = L'\0';
}


LONG
GetFileName(
    VOID
    )
{
    PFILE fh;
    LONG size;
    wchar_t szFilename[_MAX_PATH];

    SearchInclude(tokenbuf, szFilename);

    if (szFilename[0] && ((fh = _wfopen(szFilename, L"rb")) != NULL)) {
        size = MySeek(fh, 0, SEEK_END);                /* find size of file */
        MySeek(fh, 0, SEEK_SET);                       /* return to start of file */
        CtlFile(fh);
        return(size);
    }

    ParseError2(2135, tokenbuf);
    return 0;
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  AddStringToBin() -                                                      */
/*                                                                          */
/*--------------------------------------------------------------------------*/

/* adds ordinal if non-zero, otherwise adds string.  In either case, puts */
/* it in a field of NCHARS [16] */

VOID
AddStringToBin(
    USHORT ord,
    WCHAR *sz
    )
{
    USHORT      n1 = 0xFFFF;

    /* Is this an ordinal type? */
    if (ord) {
        MyWrite(fhBin, &n1, sizeof(USHORT));     /* 0xFFFF */
        MyWrite(fhBin, &ord, sizeof(USHORT));
    } else {
        MyWrite(fhBin, sz, (wcslen(sz)+1) * sizeof(WCHAR));
    }
}


PWCHAR   pTypeName[] =
{
    NULL,                //  0
    L"CURSOR",           //  1 RT_CURSOR
    L"BITMAP",           //  2 RT_BITMAP
    L"ICON",             //  3 RT_ICON
    L"MENU",             //  4 RT_MENU
    L"DIALOG",           //  5 RT_DIALOG
    L"STRING",           //  6 RT_STRING
    L"FONTDIR",          //  7 RT_FONTDIR
    L"FONT",             //  8 RT_FONT
    L"ACCELERATOR",      //  9 RT_ACCELERATOR
    L"RCDATA",           // 10 RT_RCDATA
    L"MESSAGETABLE",     // 11 RT_MESSAGETABLE
    L"GROUP_CURSOR",     // 12 RT_GROUP_CURSOR
    NULL,                // 13 RT_NEWBITMAP -- according to NT
    L"GROUP_ICON",       // 14 RT_GROUP_ICON
    NULL,                // 15 RT_NAMETABLE
    L"VERSION",          // 16 RT_VERSION
    L"DIALOGEX",         // 17 RT_DIALOGEX     ;internal
    L"DLGINCLUDE",       // 18 RT_DLGINCLUDE
    L"PLUGPLAY",         // 19 RT_PLUGPLAY
    L"VXD",              // 20 RT_VXD
    L"ANICURSOR",        // 21 RT_ANICURSOR    ;internal
    L"ANIICON",          // 22 RT_ANIICON      ;internal
    L"HTML"              // 23 RT_HTML
};

// Note: Don't forget to update the same table in rcdump.c

/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  AddBinEntry() -                                                         */
/*                                                                          */
/*--------------------------------------------------------------------------*/

/* FORMAT: type, name, flags, length, bytes */

VOID
AddBinEntry(
    PTYPEINFO pType,
    PRESINFO pRes,
    PCHAR Array,
    int ArrayCount,
    LONG FileCount
    )
{
    ULONG       hdrSize = sizeof(RESADDITIONAL);
    ULONG       t0 = 0;
    ULONG       cbPad=0;

    if (!pRes->size)
        pRes->size = ResourceSize();

    if (pType->typeord == 0) {
        hdrSize += (wcslen(pType->type) + 1) * sizeof(WCHAR);
        cbPad += (wcslen(pType->type) + 1) * sizeof(WCHAR);
    } else {
        hdrSize += 2 * sizeof(WORD);
    }

    if (pRes->nameord == 0) {
        hdrSize += (wcslen(pRes->name) + 1) * sizeof(WCHAR);
        cbPad += (wcslen(pRes->name) + 1) * sizeof(WCHAR);
    } else {
        hdrSize += 2 * sizeof(WORD);
    }

    if (cbPad % 4)
        hdrSize += sizeof(WORD);        // could only be off by 2

    if (fVerbose) {
        if (pType->typeord == 0) {
            if (pRes->nameord == 0)
                swprintf(Msg_Text, L"\nWriting %s:%s,\tlang:0x%x,\tsize %d",
                        pType->type, pRes->name, pRes->language, pRes->size);
            else
                swprintf(Msg_Text, L"\nWriting %s:%d,\tlang:0x%x,\tsize %d",
                        pType->type, pRes->nameord, pRes->language, pRes->size);
        } else {
            if (pRes->nameord == 0) {
                if (pType->typeord <= (USHORT)(UINT_PTR)RT_LAST)
                    swprintf(Msg_Text, L"\nWriting %s:%s,\tlang:0x%x,\tsize %d",
                              pTypeName[pType->typeord],
                              pRes->name, pRes->language, pRes->size);
                else
                    swprintf(Msg_Text, L"\nWriting %d:%s,\tlang:0x%x,\tsize %d",
                              pType->typeord,
                              pRes->name, pRes->language, pRes->size);
            } else {
                if (pType->typeord <= (USHORT)(UINT_PTR)RT_LAST)
                    swprintf(Msg_Text, L"\nWriting %s:%d,\tlang:0x%x,\tsize %d",
                              pTypeName[pType->typeord],
                              pRes->nameord, pRes->language, pRes->size);
                else
                    swprintf(Msg_Text, L"\nWriting %d:%d,\tlang:0x%x,\tsize %d",
                              pType->typeord,
                              pRes->nameord, pRes->language, pRes->size);
            }
        }
        fputws(Msg_Text, stdout);
    }

    if (fMacRsrcs) {
        /* record file location for the resource map and dump out
        resource's size */
        DWORD dwT;
        pRes->BinOffset = (long)MySeek(fhBin,0L,1) - MACDATAOFFSET;
        dwT = SwapLong(pRes->size);
        MyWrite(fhBin, &dwT, 4);
    } else {
        /* add type, name, flags, and resource length */
        MyWrite(fhBin, &pRes->size, sizeof(ULONG));
        MyWrite(fhBin, &hdrSize, sizeof(ULONG));

        AddStringToBin(pType->typeord, pType->type);
        AddStringToBin(pRes->nameord , pRes->name);
        MyAlign(fhBin);

        MyWrite(fhBin, &t0, sizeof(ULONG));  /* data version */
        MyWrite(fhBin, &pRes->flags, sizeof(WORD));
        MyWrite(fhBin, &pRes->language, sizeof(WORD));
        MyWrite(fhBin, &pRes->version, sizeof(ULONG));
        MyWrite(fhBin, &pRes->characteristics, sizeof(ULONG));

        /* record file location for the .EXE construction */
        pRes->BinOffset = (LONG)MySeek(fhBin, 0L, SEEK_CUR);
    }

    /* write array plus contents of resource source file */
    WriteControl(fhBin, Array, ArrayCount, FileCount);
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  AddResToResFile(pType, pRes, Array, ArrayCount, FileCount)              */
/*                                                                          */
/*  Parameters:                                                             */
/*      pType  : Pointer to Res Type                                        */
/*      pRes   : Pointer to resource                                        */
/*      Array  : Pointer to array from which some data is to be copied into */
/*               the .RES file.                                             */
/*               This is ignored if ArrayCount is zero.                     */
/*      ArrayCount : This is the number of bytes to be copied from "Array"  */
/*                   into the .RES file. This is zero if no copy is required*/
/*      FileCount  : This specifies the number of bytes to be copied from   */
/*                   fhCode into fhOut. If this is -1, the complete input   */
/*                   file is to be copied into fhOut.                       */
/*                                                                          */
/*------------------------------------------------------------------------*/

VOID
AddResToResFile(
    PTYPEINFO pType,
    PRESINFO pRes,
    PCHAR Array,
    int ArrayCount,
    LONG FileCount
    )
{
    PRESINFO p;

    p = pType->pres;

    /* add resource to end of resource list for this type */
    if (p) {
        while (p->next)
            p = p->next;

        p->next = pRes;
    } else {
        pType->pres = pRes;
    }


    /* add the resource to the .RES File */
    AddBinEntry(pType, pRes, Array, ArrayCount, FileCount);

    /* keep track of number of resources and types */
    pType->nres++;
    ResCount++;
    WriteResInfo(pRes, pType, TRUE);
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  AddResType() -                                                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

PTYPEINFO
AddResType(
    PWCHAR s,
    LPWSTR l
    )
{
    PTYPEINFO  pType;

    if ((pType = pTypInfo) != 0) {
        for (; ; ) {
            /* search for resource type, return if already exists */
            if ((s && pType->type && !wcscmp(s, pType->type)) ||
                (!s && l && pType->typeord == (USHORT)l))
                return(pType);
            else if (!pType->next)
                break;
            else
                pType = pType->next;
        }

        /* if not in list, add space for it */
        pType->next = (PTYPEINFO)MyAlloc(sizeof(TYPEINFO));
        pType = pType->next;
    } else {
        /* allocate space for resource list */
        pTypInfo = (PTYPEINFO)MyAlloc(sizeof(TYPEINFO));
        pType = pTypInfo;
    }

    /* fill allocated space with name and ordinal, and clear the resources
         of this type */
    pType->type = MyMakeStr(s);
    pType->typeord = (USHORT)l;
    pType->nres = 0;
    pType->pres = NULL;

    return(pType);
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  DGetMemFlags() -                                                        */
/*                                                                          */
/*--------------------------------------------------------------------------*/

int
DGetMemFlags (
    PRESINFO pRes
    )
{
    if (token.type == NUMLIT)
        // this is a numeric value, not a mem flag -- this means we're done
        // processing memory flags
        return(FALSE);

    /* adjust memory flags of resource */
    switch (token.val) {
        case TKMOVEABLE:
            pRes->flags |= NSMOVE;
            break;

        case TKFIXED:
            pRes->flags &= ~(NSMOVE | NSDISCARD);
            break;

        case TKPURE :
            pRes->flags |= NSPURE;
            break;

        case TKIMPURE :
            pRes->flags &= ~(NSPURE | NSDISCARD);
            break;

        case TKPRELOAD:
            pRes->flags |= NSPRELOAD;
            break;

        case TKLOADONCALL:
            pRes->flags &= ~NSPRELOAD;
            break;

        case TKDISCARD:
            pRes->flags |= NSMOVE | NSPURE | NSDISCARD;
            break;

        case TKEXSTYLE:
            GetToken(FALSE);        /* ignore '=' */
            if (token.type != EQUAL)
                ParseError1(2136);
            GetTokenNoComma(TOKEN_NOEXPRESSION);
            GetFullExpression(&pRes->exstyleT, GFE_ZEROINIT);
            break;

            /* if current token not memory flag, return FALSE to indicate not
             to continue parsing flags */
        default:
            return(FALSE);
    }

    GetToken(FALSE);

    /* TRUE ==> found memory flag */
    return(TRUE);
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  AddDefaultTypes() -                                                     */
/*                                                                          */
/*--------------------------------------------------------------------------*/

VOID
AddDefaultTypes(
    VOID
    )
{
    AddResType(L"CURSOR", RT_GROUP_CURSOR);
    AddResType(L"ICON", RT_GROUP_ICON);
    AddResType(L"BITMAP", RT_BITMAP);
    AddResType(L"MENU", RT_MENU);
    AddResType(L"DIALOG", RT_DIALOG);
    AddResType(L"STRINGTABLE", RT_STRING);
    AddResType(L"FONTDIR", RT_FONTDIR);
    AddResType(L"FONT", RT_FONT);
    AddResType(L"ACCELERATORS", RT_ACCELERATOR);
    AddResType(L"RCDATA", RT_RCDATA);
    AddResType(L"MESSAGETABLE", RT_MESSAGETABLE);
    AddResType(L"VERSIONINFO", RT_VERSION);
    AddResType(L"DLGINCLUDE", RT_DLGINCLUDE);
    AddResType(L"MENUEX", RT_MENUEX);
    AddResType(L"DIALOGEX", RT_DIALOGEX);
    AddResType(L"PLUGPLAY", RT_PLUGPLAY);
    AddResType(L"VXD", RT_VXD);

    // AFX resource types.
    AddResType(L"DLGINIT", RT_DLGINIT);
    AddResType(L"TOOLBAR", RT_TOOLBAR);

    AddResType(L"ANIICON",   RT_ANIICON);
    AddResType(L"ANICURSOR", RT_ANICURSOR);

    AddResType(L"HTML", RT_HTML);
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  AddFontDir() -                                                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

VOID
AddFontDir(
    VOID
    )
{
    PRESINFO   pRes;
    PTYPEINFO  pType;
    PFONTDIR   pFont;

    /* make new resource */
    pRes = (PRESINFO)MyAlloc(sizeof(RESINFO));
    pRes->language = language;
    pRes->version = version;
    pRes->characteristics = characteristics;
    pRes->name = MyMakeStr(L"FONTDIR");

    /* find or create the type list */
    pType = AddResType(NULL, RT_FONTDIR);

    CtlInit();

    WriteWord(nFontsRead);

    pFont = pFontList;

    while (pFont) {
        WriteWord(pFont->ordinal);
        WriteBuffer(pFont + 1, pFont->nbyFont);
        pFont = pFont->next;
    }

    pRes->flags = NSMOVE | NSPRELOAD;

    /* write to the .RES file */
    SaveResFile(pType, pRes);
}


/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  ReadRF() -                                                              */
/*                                                                          */
/*--------------------------------------------------------------------------*/

/* top level parsing function: recognizes an RC script */

int
ReadRF(
    VOID
    )
{
    PRESINFO    pRes;
    PTYPEINFO   pType;
    ULONG       zero=0;
    WORD        ffff=0xffff;
    ULONG       hdrSize = sizeof(RESADDITIONAL) + 2 * (sizeof(WORD) * 2);

    ResCount = 0;
    nFontsRead = 0;

    /* Initialize data structures. */
    AddDefaultTypes();

    if (!fMacRsrcs) {
        /* write 32-bit header for empty resource/signature */
        MyWrite(fhBin, &zero, sizeof(ULONG));
        MyWrite(fhBin, &hdrSize, sizeof(ULONG));
        MyWrite(fhBin, &ffff, sizeof(WORD));
        MyWrite(fhBin, &zero, sizeof(WORD));
        MyWrite(fhBin, &ffff, sizeof(WORD));
        MyWrite(fhBin, &zero, sizeof(WORD));
        MyWrite(fhBin, &zero, sizeof(ULONG));
        MyWrite(fhBin, &zero, sizeof(WORD));
        MyWrite(fhBin, &zero, sizeof(WORD));
        MyWrite(fhBin, &zero, sizeof(ULONG));
        MyWrite(fhBin, &zero, sizeof(ULONG));
    }

    CtlAlloc();

    if (fAFXSymbols) {
        int n;
        char szMultiByte[_MAX_PATH];
        char *pch = szMultiByte;
        // write out first HWB resource

        CtlInit();
        pRes = (PRESINFO)MyAlloc(sizeof(RESINFO));
        pRes->language = language;
        pRes->version = version;
        pRes->characteristics = characteristics;

        pRes->size = sizeof(DWORD);
        pRes->flags = 0;
        pRes->name = 0;
        pRes->nameord = 1;
        WriteLong(0);           /* space for file pointer */

        n = wcslen(inname) + 1;
        n = WideCharToMultiByte(CP_ACP,
                                0,
                                inname,
                                n,
                                szMultiByte,
                                MAX_PATH,
                                NULL,
                                NULL);

        while (*pch) {
            WriteByte(*pch++);
            pRes->size++;
        }
        WriteByte(0);
        pRes->size++;

        pType = AddResType(L"HWB", 0);
        SaveResFile(pType, pRes);
        lOffIndex = pRes->BinOffset;
    }

    /* Process the RC file. */
    do {
        token.sym.name[0] = L'\0';
        token.sym.nID = 0;

        /* Find the beginning of the next resource. */
        if (!GetNameOrd())
            break;

        if (!wcscmp(tokenbuf, L"LANGUAGE")) {
            language = GetLanguage();
            continue;
        } else if (!wcscmp(tokenbuf, L"VERSION")) {
            GetToken(FALSE);
            if (token.type != NUMLIT)
                ParseError1(2139);
            version = token.longval;
            continue;
        } else if (!wcscmp(tokenbuf, L"CHARACTERISTICS")) {
            GetToken(FALSE);
            if (token.type != NUMLIT)
                ParseError1(2140);
            characteristics = token.longval;
            continue;
        }

        /* Print a dot for each resource processed. */
        if (fVerbose) {
            wprintf(L".");
        }

        /* Allocate space for the new resources Info structure. */
        pRes = (PRESINFO)MyAlloc(sizeof(RESINFO));
        pRes->language = language;
        pRes->version = version;
        pRes->characteristics = characteristics;

        if (token.sym.name[0]) {
            /* token has a real symbol associated with it */
            memcpy(&pRes->sym, &token.sym, sizeof(SYMINFO));
        } else {
            pRes->sym.name[0] = L'\0';
        }

        if (!token.val) {
            if (wcslen(tokenbuf) > MAXTOKSTR-1) {
                SET_MSG(4206, curFile, token.row);
                SendError(Msg_Text);
                tokenbuf[MAXTOKSTR-1] = L'\0';
                token.val = MAXTOKSTR-2;
            }
            pRes->name = MyMakeStr(tokenbuf);
        } else {
            pRes->nameord = token.val;
        }

      /* If not a string table, find out what kind of resource follows.
       * The StringTable is a special case since the Name field is the
       * string's ID number mod 16.
       */
        if ((pRes->name == NULL) || wcscmp(pRes->name, L"STRINGTABLE")) {
            if (!GetNameOrd())
                break;

            if (!token.val) {
                if (wcslen(tokenbuf) > MAXTOKSTR-1) {
                    SET_MSG(4207, curFile, token.row);
                    SendError(Msg_Text);
                    tokenbuf[MAXTOKSTR-1] = L'\0';
                    token.val = MAXTOKSTR-2;
                }
                if (!wcscmp(tokenbuf, L"STRINGTABLE")) {
                    // User attempted to create a named string table... Bail
                    ParseError1(2255);
                    pRes->name = MyMakeStr(tokenbuf);
                    goto ItsAStringTable;
                }

                pType = AddResType(tokenbuf, MAKEINTRESOURCE(0));
            }
            else
                pType = AddResType(NULL, MAKEINTRESOURCE(token.val));

            if (!pType)
                return(errorCount == 0);

            /* Parse any user specified memory flags. */
            GetToken(FALSE);

            switch ((INT_PTR)pType->typeord) {
                    /* Calculated resources default to discardable. */
                case (INT_PTR)RT_ICON:
                case (INT_PTR)RT_CURSOR:
                case (INT_PTR)RT_FONT:
                case (INT_PTR)RT_DIALOG:
                case (INT_PTR)RT_MENU:
                case (INT_PTR)RT_DLGINCLUDE:
                case (INT_PTR)RT_DIALOGEX:
                case (INT_PTR)RT_MENUEX:
                    pRes->flags = NSMOVE | NSPURE | NSDISCARD;
                    break;

                case (INT_PTR)RT_GROUP_ICON:
                case (INT_PTR)RT_GROUP_CURSOR:
                    pRes->flags = NSMOVE | NSDISCARD;
                    break;

                    /* All other resources default to moveable. */
                default:
                    pRes->flags = NSMOVE | NSPURE;
                    break;
            }

            /* adjust according to the user's specifications
           */
            while (DGetMemFlags(pRes))
                ;

            // write out start of new resource
            WriteResInfo(pRes, pType, FALSE);
        } else {

ItsAStringTable:

            /* Parse any user specified memory flags. */
            GetToken(FALSE);

            /* String and Error resources default to discardable. */
            pRes->flags = NSMOVE | NSPURE | NSDISCARD;
            while (DGetMemFlags(pRes))
                ;

            pType = NULL;
        }

        if (!pType) {
            /* parse the string table, if that's what it is */
            if ((pRes->name != NULL) && (!wcscmp(pRes->name, L"STRINGTABLE"))) {
                if (GetTable(pRes) == NULL)
                    break;
            } else {
                ParseError1(2141);
            }
        } else {
            CtlInit();
            pRes->size = 0L;

            /* call parsing and generating functions specific to the various
             resource types */
            switch ((INT_PTR)pType->typeord) {
                case (INT_PTR)RT_DIALOGEX:
                    /* allocate dialog memory */
                    pLocDlg = (PDLGHDR) MyAlloc(sizeof(DLGHDR));

                    /* parse dialog box */
                    GetDlg(pRes, pLocDlg, TRUE);

                    /* write dialog box */
                    SaveResFile(AddResType(L"DIALOG", 0), pRes);

                    /* free dialog memory */
                    MyFree(pLocDlg);
                    break;

                case (INT_PTR)RT_DIALOG:
                    /* allocate dialog memory */
                    pLocDlg = (PDLGHDR) MyAlloc(sizeof(DLGHDR));

                    /* parse dialog box */
                    GetDlg(pRes, pLocDlg, FALSE);

                    /* write dialog box */
                    SaveResFile(pType, pRes);

                    /* free dialog memory */
                    MyFree(pLocDlg);
                    break;

                case (INT_PTR)RT_ACCELERATOR:
                    GetAccelerators(pRes);
                    SaveResFile(pType, pRes);
                    break;

                case (INT_PTR)RT_MENUEX:
                    WriteWord(MENUITEMTEMPLATEVERSIONNUMBER);
                    WriteWord(MENUITEMTEMPLATEBYTESINHEADER);
                    ParseMenu(FALSE, pRes);
                    SaveResFile(AddResType(L"MENU", 0), pRes);
                    break;

                case (INT_PTR)RT_MENU:
                    WriteWord(OLDMENUITEMTEMPLATEVERSIONNUMBER);
                    WriteWord(OLDMENUITEMTEMPLATEBYTESINHEADER);
                    ParseOldMenu(FALSE, pRes);
                    SaveResFile(pType, pRes);
                    break;

                case (INT_PTR)RT_ICON:
                case (INT_PTR)RT_CURSOR:
                    WriteFileInfo(pRes, pType, tokenbuf);
                    pRes->size = GetFileName();
                    if (pRes->size) {
                        if (FileIsAnimated(pRes->size)) {
                            goto ani;
                        } else {
                            pRes->size = GetIcon(pRes->size);
                            SaveResFile(pType, pRes);
                        }
                    }
                    break;

                case (INT_PTR) RT_ANIICON:
                case (INT_PTR) RT_ANICURSOR:
ani:
                    {
                        USHORT iLastTypeOrd = pType->typeord;

                        // Strictly speaking, ANIICON and ANICURSOR are not allowed.  However,
                        // we'll keep them around for the time being.  BryanT 8/14/96.
                        if ((pType->typeord == (USHORT)(INT_PTR)RT_ICON) ||
                            (pType->typeord == (USHORT)(INT_PTR)RT_GROUP_ICON))
                        {
                            pType->typeord = (USHORT)(INT_PTR)RT_ANIICON;
                        } else
                        if ((pType->typeord == (USHORT)(INT_PTR) RT_CURSOR) ||
                            (pType->typeord == (USHORT)(INT_PTR) RT_GROUP_CURSOR))
                        {
                            pType->typeord = (USHORT)(INT_PTR)RT_ANICURSOR;
                        }
                        WriteFileInfo(pRes, pType, tokenbuf);
                        pRes->size = GetFileName();
                        if (pRes->size) {
                            pRes->size = GetAniIconsAniCursors(pRes->size);
                            SaveResFile(pType, pRes);
                        }

                        pType->typeord = iLastTypeOrd;
                    }
                    break;

                case (INT_PTR)RT_BITMAP:
                    WriteFileInfo(pRes, pType, tokenbuf);
                    pRes->size = GetFileName();
                    if (pRes->size) {
                        /* Bitmap in DIB format */
                        pRes ->size = GetNewBitmap();
                        SaveResFile(pType, pRes);
                    }
                    break;

                case (INT_PTR)RT_GROUP_ICON:
                    WriteFileInfo(pRes, pType, tokenbuf);
                    pRes->size = GetFileName();
                    if (pRes->size) {
                        if (FileIsAnimated(pRes->size)) {
                            goto ani;
                        } else {
                            if (fMacRsrcs)
                                GetMacIcon(pType, pRes);
                            else
                                GetNewIconsCursors(pType, pRes, RT_ICON);
                        }
                    }
                    break;

                case (INT_PTR)RT_GROUP_CURSOR:
                    WriteFileInfo(pRes, pType, tokenbuf);
                    pRes->size = GetFileName();
                    if (pRes->size) {
                        if (FileIsAnimated(pRes->size)) {
                            goto ani;
                        } else {
                            if (fMacRsrcs)
                                GetMacCursor(pType, pRes);
                            else
                                GetNewIconsCursors(pType, pRes, RT_CURSOR);
                        }
                    }
                    break;

                case (INT_PTR)RT_FONT:
                    WriteFileInfo(pRes, pType, tokenbuf);
                    pRes->size = GetFileName();
                    if (pRes->name)
                        ParseError1(2143);
                    if (AddFontRes(pRes)) {
                        nFontsRead++;
                        SaveResFile(pType, pRes);
                    }
                    break;

                case (INT_PTR)RT_FONTDIR:
                    WriteFileInfo(pRes, pType, tokenbuf);
                    fFontDirRead = TRUE;
                    pRes->size = GetFileName();
                    if (pRes->size) {
                        SaveResFile(pType, pRes);
                    }
                    break;

                case (INT_PTR)RT_MESSAGETABLE:
                    pRes->size = GetFileName();
                    if (pRes->size) {
                        SaveResFile(pType, pRes);
                    }
                    break;

                case (INT_PTR)RT_VERSION:
                    VersionParse();
                    SaveResFile(pType, pRes);
                    break;

                case (INT_PTR)RT_DLGINCLUDE:
                    DlgIncludeParse(pRes);
                    SaveResFile(pType, pRes);
                    break;

                case (INT_PTR)RT_TOOLBAR:
                    GetToolbar(pRes);
                    SaveResFile(pType, pRes);
                    break;

                case (INT_PTR)RT_RCDATA:
                case (INT_PTR)RT_DLGINIT:
                default:
                    if (token.type != BEGIN) {
                        pRes->size = GetFileName();
                        if (pRes->size) {
                            WriteFileInfo(pRes, pType, tokenbuf);
                        }
                    } else {
                        RESINFO_PARSE rip;

                        bExternParse = FALSE;

                        // Check to see if caller wants to parse this.
                        if ((lpfnParseCallbackA != 0) || (lpfnParseCallbackW != 0)) {
                            rip.size = 0L;
                            rip.type = pType->type;
                            rip.typeord = pType->typeord;
                            rip.name = pRes->name;
                            rip.nameord = pRes->nameord;
                            rip.flags = pRes->flags;
                            rip.language = pRes->language;
                            rip.version = pRes->version;
                            rip.characteristics = pRes->characteristics;

                            if (lpfnParseCallbackW != 0) {
                                bExternParse = (*lpfnParseCallbackW)(&rip, NULL, NULL);
                            } else {
                                bExternParse = (*lpfnParseCallbackA)(&rip, NULL, NULL);
                            }
                        }

                        if (!bExternParse) {
                            GetRCData(pRes);
                        } else {
                            union {
                                CONTEXTINFO_PARSE cipA;
                                CONTEXTINFO_PARSEW cipW;
                            } cip;

                            extern PCHAR CodeArray;
                            extern int CodeSize;
                            extern int CCount;

                            int nBegins = 1;
                            int nCountSave; // CCount before END token reached

                            cip.cipW.hHeap = hHeap;
                            cip.cipW.hWndCaller = hWndCaller;
                            cip.cipW.line = token.row;

                            // Collect data for caller to parse.
                            while(nBegins > 0) {
                                nCountSave = CCount;
                                GetToken(FALSE);

                                if (token.type == BEGIN) {
                                    nBegins++;
                                } else if (token.type == END) {
                                    nBegins--;
                                }
                            }

                            bExternParse = FALSE;

                            if ((rip.size = nCountSave) > 0) {
                                BOOL b;

                                if (lpfnParseCallbackW != 0) {
                                    wchar_t mbuff[512];    // REVIEW: Long filenames??  See error.c also.

                                    cip.cipW.lpfnMsg = lpfnMessageCallbackW;

                                    swprintf(mbuff, L"%s(%%d) : %%s", curFile);
                                    cip.cipW.format = mbuff;

                                    b = (*lpfnParseCallbackW)(&rip, (void **) &CodeArray, &cip.cipW);
                                } else {
                                    char mbuff[512];    // REVIEW: Long filenames??  See error.c also.

                                    cip.cipA.lpfnMsg = lpfnMessageCallbackA;

                                    sprintf(mbuff, "%S(%%d) : %%s", curFile);
                                    cip.cipA.format = mbuff;

                                    b = (*lpfnParseCallbackA)(&rip, (void **) &CodeArray, &cip.cipA);
                                }

                                if (!b) {
                                    // Assume caller gave error message, and quit.

                                    quit(NULL);
                                }
                            }

                        pRes->size = CCount = CodeSize = rip.size;
                        }
                    }

                    SaveResFile(pType, pRes);
                    break;
            }
            // write out end of new resource
            WriteResInfo(NULL, NULL, FALSE);
        }
    } while (token.type != EOFMARK);

    /* if we added fonts without a font directory, add one */
    if (!fFontDirRead && nFontsRead)
        AddFontDir();

    /* write string table */
    if (pResString != NULL)
        WriteTable(pResString);

    /* write Mac resource map */
    if (fMacRsrcs)
        WriteMacMap();

    CtlFree();

    if (fVerbose) {
        wprintf(L"\n");
    }
    return(errorCount == 0);
}


WORD
GetLanguage()
{
    WORD    L_language;

    GetToken(FALSE);
    if (token.type != NUMLIT) {
        ParseError1(2144);
        return MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
    }
    if (token.flongval) {
        ParseError1(2145);
        return MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
    }
    L_language = token.val;
    GetToken(FALSE);
    if (token.type != COMMA) {
        ParseError1(2146);
        return MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
    }
    GetToken(FALSE);
    if (token.type != NUMLIT) {
        ParseError1(2147);
        return MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
    }
    if (token.flongval) {
        ParseError1(2148);
        return MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
    }

    return MAKELANGID(L_language, token.val);
}
